/*
 *  $Id: DataLoader.cs 1198 2010-11-20 14:32:09Z thenn.erannor $
 *	This file is a part of Ragima CCG.
 *	(C) 2008-2010 Ragima development group.
 *
 *	Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System;
using System.Collections.Generic;
using System.Reflection;

using Nereal.Extensions;

namespace Ragima {
    /// <summary>
    /// Загрузчик файлов данных.
    /// </summary>
    public sealed class DataLoader {
        private static Dictionary<Type, DataFileLoader> _knownTypes = new Dictionary<Type, DataFileLoader>();

        public static void RegisterType(Type dataType, DataFileLoader loader) {
            _knownTypes.Add(dataType, loader);
        }

        public static bool IsKnownType(Type dataType) {
            return _knownTypes.ContainsKey(dataType);
        }

        private static DataFileLoader GetTypeLoader(Type dataType) {
            return _knownTypes.GetOrDefault(dataType);
        }

        private Type _type;
        private List<FieldInfo> _staticFields, _instanceFields;
        private List<DataLoader> _nestedClasses;
        private int _countStatic, _countInstance, _countAllStatic;
        private SimpleEvent _onLoad;
        private IDataCollector _collector;

        /// <summary>
        /// Загрузчик статических полей указанного типа.
        /// </summary>
        /// <param name="type">Загружаемый тип</param>
        public DataLoader(Type type, SimpleEvent onLoad, IDataCollector collector) {
            _type = type;
            _staticFields = new List<FieldInfo>();
            _instanceFields = new List<FieldInfo>();
            _nestedClasses = new List<DataLoader>();
            _countStatic = 0;
            _countInstance = 0;
            _countAllStatic = 0;
            _onLoad = onLoad;
            _collector = collector;
            Initialize();
        }

        private void Initialize() {
            foreach (var field in _type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)) {
                var attr = field.GetAttribute<DataFileAttribute>(true);
                if (attr != null) {
                    if (field.IsStatic) {
                        _staticFields.Add(field);
                        _countStatic += attr.CountLoadedItems;
                    } else {
                        _instanceFields.Add(field);
                        _countInstance += attr.CountLoadedItems;
                    }
                }
            }
            _countAllStatic = _countStatic;
            foreach (var nestedType in _type.GetNestedTypes(BindingFlags.Public | BindingFlags.Static)) {
                if (nestedType.IsClass) {
                    var loader = new DataLoader(nestedType, _onLoad, _collector);
                    _nestedClasses.Add(loader);
                    _countAllStatic += loader.CountAllStatic;
                }
            }
        }

        public Type LoadedType {
            get { return _type; }
        }

        public int CountStatic {
            get { return _countStatic; }
        }
        public int CountAllStatic {
            get { return _countAllStatic; }
        }
        public int CountInstance {
            get { return _countInstance; }
        }

        public void LoadStatic() {
            LoadStatic(string.Empty);
        }
        public void LoadStatic(string prefix) {
            LoadFields(_staticFields, null, prefix);
            foreach (var loader in _nestedClasses)
                loader.LoadStatic(prefix + loader.LoadedType.Name + ".");
        }
        public void LoadInstance(object obj) {
            LoadFields(_instanceFields, obj, string.Empty);
        }

        private void LoadFields(IEnumerable<FieldInfo> fields, object obj, string prefix) {
            foreach (var field in fields) {
                var attr = field.GetAttribute<DataFileAttribute>(true);
                var loader = GetTypeLoader(field.FieldType);
                var data = attr.LoadData(loader);
                field.SetValue(obj, data);
                if (_collector != null)
                    _collector.Collect(prefix + field.Name, data);
                if (_onLoad != null) {
                    for (int i = 0; i < attr.CountLoadedItems; i++)
                        _onLoad();
                }
            }
        }
    }

    public interface IDataCollector {
        void Collect(string id, object data);
    }

    /// <summary>
    /// Делегат загрузки файла данных.
    /// </summary>
    /// <param name="filename">Имя файла</param>
    /// <returns>Загруженный объект</returns>
    public delegate object DataFileLoader(string filename);

    /// <summary>
    /// Атрибут имени файла данных.
    /// </summary>
    public class DataFileAttribute: Attribute {
        private string _filename;

        public DataFileAttribute(string filename) {
            _filename = filename;
        }

        public string FileName {
            get { return _filename; }
        }

        public virtual int CountLoadedItems {
            get { return 1; }
        }

        public virtual object LoadData(DataFileLoader defaultLoader) {
            return defaultLoader != null ? defaultLoader(FileName) : null;
        }
    }
}
